import axios, { AxiosError, AxiosRequestConfig, Canceler } from 'axios';
import useStore from '../store/modules/User';

// 负责生成Map中URL对应的key值
const generateURLKey = (config: AxiosRequestConfig) =>
  ['cancel-url', config.method, config.url].join('&');

const axiosConfig: AxiosRequestConfig = {
  baseURL: '/api/', // api的base URL
  timeout: 10000, // 设置请求超时时间
  responseType: 'json',
  withCredentials: true, // 是否允许带cookie这些
  headers: {
    'Content-Type': 'application/json;charset=utf-8', // 传输数据类型
    'Access-Control-Allow-Origin': '*', // 允许跨域
  },
};

// 创建axios实例
const axiosInstance = axios.create(axiosConfig);

// 用于存放未完成请求的队列
const pending = new Map<string, Canceler>();

// 向Map中添加当前网络请求
const addPending = (aConfig: AxiosRequestConfig) => {
  const config = aConfig;
  const url = generateURLKey(config);
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel: Canceler) => {
      if (!pending.has(url)) {
        // 如果 pending 中不存在当前请求，则添加进去
        pending.set(url, cancel);
      }
    });
};

// 从Map中删除网络请求
const removePending = (config: AxiosRequestConfig) => {
  const url = generateURLKey(config);
  if (pending.has(url)) {
    // 如果在 pending 中存在当前请求标识，需要取消当前请求，并且移除
    const cancel: Canceler = pending.get(url) as Canceler;
    cancel(url);
    pending.delete(url);
  }
};

// 清空Map中所有的请求标识，在Vue router中切换路由时调用
export const clearPending = () => {
  pending.forEach((item: Canceler) => {
    item('switch router');
  });
  pending.clear();
};

axiosInstance.interceptors.request.use(
  // 在发送请求之前调用
  (config: AxiosRequestConfig): AxiosRequestConfig => {
    removePending(config); // 在请求开始前，对之前的请求做检查取消操作
    addPending(config); // 将当前请求添加到 pending 中
    const newConfig = config;
    // 调用 User Store
    const store = useStore();
    // 将用户token添加到请求中
    if (store.token) {
      if (!newConfig.headers) newConfig.headers = {};
      Object.assign(newConfig.headers, { 'peekpa-token': store.token });
    }
    return newConfig;
  },
  (error: AxiosError): Promise<never> => {
    // 对请求错误时调用，可自己定义
    return Promise.reject(error);
  }
);

export { axiosInstance, axiosConfig };
